home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
dev
/
ds3100.md
/
devSII.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
37KB
|
1,375 lines
/*
* devSII.c --
*
* The driver for the SII chip.
*
* Copyright (C) 1989 Digital Equipment Corporation.
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appears in all copies.
* Digital Equipment Corporation makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/dev/ds3100.md/devSII.c,v 9.3 91/08/19 13:55:10 jhh Exp $ SPRITE (DECWRL)";
#endif not lint
#include <sprite.h>
#include <sii.h>
#include <mach.h>
#include <dev.h>
#include <devInt.h>
#include <scsiHBA.h>
#include <scsiDevice.h>
#include <sys/scsi.h>
#include <sync.h>
#include <stdlib.h>
#include <user/fs.h>
#define RESET TRUE
/*
* MACROS for timing out spin loops.
*
* Waits while expression is true.
*
* args: expr - expression to spin on
* spincount - amount of time to wait in microseconds
* retval - return value of this macro
*/
#define SII_WAIT_WHILE(expr,spincount,retval) { \
for (retval = 0; ((retval < 100) && (expr)); retval++) {\
} \
while ((expr) && ( retval < spincount)) { \
MACH_DELAY(100); \
retval++; \
} \
}
/*
* Waits unitl expression is true.
*/
#define SII_WAIT_UNTIL(expr,spincount,retval) { \
SII_WAIT_WHILE(!(expr),spincount,retval); \
}
/*
* The different phases.
*/
#define MSG_IN_PHASE 0x7
#define MSG_OUT_PHASE 0x6
#define STATUS_PHASE 0x3
#define CMD_PHASE 0x2
#define DATA_IN_PHASE 0x1
#define DATA_OUT_PHASE 0x0
/*
* Forward declaration.
*/
typedef struct Controller Controller;
/*
* Device - The data structure containing information about a device. One of
* these structure is kept for each attached device. Note that is structure
* is casted into a ScsiDevice and returned to higher level software.
* This implies that the ScsiDevice must be the first field in this
* structure.
*/
typedef struct Device {
ScsiDevice handle; /* Scsi Device handle. This is the only part
* of this structure visible to higher
* level software. MUST BE FIRST FIELD IN STRUCTURE. */
int targetID;/* SCSI Target ID of this device. Note that
* the LUN is store in the device handle. */
Controller *ctrlPtr;/* Controller to which device is attached. */
/*
* The following part of this structure is
* used to handle SCSI commands that return
* CHECK status. To handle the REQUEST SENSE
* command we must: 1) Save the state of the current
* command into the "struct FrozenCommand". 2) Submit
* a request sense command formatted in SenseCmd
* to the device.
*/
unsigned buffOffset;
struct FrozenCommand {
ScsiCmd *scsiCmdPtr; /* The frozen command. */
unsigned char statusByte; /* It's SCSI status byte, Will always have
* the check bit set.
*/
int amountTransferred; /* Number of bytes transferred by this
* command.
*/
int status; /* Status of frozen command. */
} frozen;
char senseBuffer[DEV_MAX_SENSE_BYTES]; /* Data buffer for request sense */
ScsiCmd SenseCmd; /* Request sense command buffer. */
} Device;
/*
* Controller - The Data structure describing an SII controller.
*/
struct Controller {
volatile SIIRegs *regsPtr; /* Pointer to the registers of this controller.*/
int dmaState; /* DMA state for this controller, defined below. */
Boolean dmaStarted; /* TRUE => dma was started. */
char *name; /* String for error message for this controller. */
DevCtrlQueues devQueues; /* Device queues for devices attached to this
* controller. */
Address ramBuff; /* DMA memory. */
Sync_Semaphore mutex; /* Lock protecting controller's data structures. */
/* Until disconnect/reconnect is added we can have
* only one current active device and scsi command.*/
Device *devPtr; /* Current active command. */
ScsiCmd *scsiCmdPtr; /* Current active command. */
Device *devicePtr[8][8]; /* Pointers to the device attached to the
* controller index by [targetID][LUN].
* NIL if device not attached yet. Zero if
* device conflicts with HBA address. */
};
/*
* Possible values for the dmaState state field of a controller.
*
* DMA_RECEIVE - data is being received from the device, such as on
* a read, inquiry, or request sense.
* DMA_SEND - data is being send to the device, such as on a write.
* DMA_INACTIVE - no data needs to be transferred.
*/
#define DMA_RECEIVE 0
#define DMA_SEND 1
#define DMA_INACTIVE 2
/*
* MAX_SII_CTRLS - Maximum number of SII controllers attached to the
* system.
*/
#define MAX_SII_CTRLS 1
static Controller *controllers[MAX_SII_CTRLS];
/*
* Highest number controller we have probed for.
*/
static int numSIIControllers = 0;
int devSIIDebug = 1;
/*
* Forward declarations.
*/
static void Reset();
static ReturnStatus SendCommand();
static ReturnStatus GetStatusByte();
static ReturnStatus WaitPhase();
static ReturnStatus RecvBytes();
static void PrintRegs();
static ReturnStatus StartDMA();
static char *PhaseName();
/*
*----------------------------------------------------------------------
*
* Reset --
*
* Reset a SCSI bus controlled by the SII.
*
* Results:
* None.
*
* Side effects:
* Reset the controller and SCSI bus.
*
*----------------------------------------------------------------------
*/
static void
Reset(ctrlPtr)
Controller *ctrlPtr;
{
volatile SIIRegs *regsPtr = ctrlPtr->regsPtr;
/*
* Reset the SII chip.
*/
regsPtr->comm = SII_CHRESET;
/*
* Set arbitrated bus mode.
*/
regsPtr->csr = SII_HPM;
/*
* SII is always ID 7.
*/
regsPtr->id = SII_ID_IO | 7;
/*
* Enable SII to drive SCSI bus and turn off synchronous commands.
*/
regsPtr->dictrl = SII_PRE;
regsPtr->dmctrl = 0;
/*
* Assert SCSI bus reset for at least 25 Usec to clear the
* world. SII_RST is self clearing.
*/
regsPtr->comm = SII_RST;
MACH_DELAY(25);
/*
* Clear any pending interrupts from the reset.
*/
regsPtr->cstat = regsPtr->cstat;
regsPtr->dstat = regsPtr->dstat;
/*
* Set up SII for arbitrated bus mode, SCSI parity checking,
* Select Enable, Reselect Enable, and Interrupt Enable.
*/
regsPtr->csr = (SII_HPM | SII_RSE | SII_SLE | SII_PCE | SII_IE);
MACH_DELAY(5 * 1000000);
}
/*
*----------------------------------------------------------------------
*
* SelectTarget --
*
* Select a target.
*
* Results:
* SUCCESS if could select, FAILURE if couldn't.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
SelectTarget(devPtr)
Device *devPtr; /* Device we want to select target for. */
{
volatile register SIIRegs *regsPtr;
int retval, i;
regsPtr = devPtr->ctrlPtr->regsPtr;
/* Loop till retries exhausted */
for(i=0; i < 2; i++) {
regsPtr->slcsr = devPtr->targetID;
regsPtr->comm = SII_SELECT;
Mach_EmptyWriteBuffer();
/*
* Start timer to wait for a select to occur
*/
SII_WAIT_UNTIL(regsPtr->cstat & SII_SCH, SII_WAIT_COUNT/4, retval);
/*
* If a state change did occur then make sure we are connected
*/
if((regsPtr->cstat & SII_SCH) && !(regsPtr->cstat & SII_CON)) {
SII_WAIT_UNTIL((regsPtr->cstat & SII_CON),SII_WAIT_COUNT,retval);
}
if(retval >= SII_WAIT_COUNT || !(regsPtr->cstat & SII_CON)) {
regsPtr->cstat = SII_SCH;
Mach_EmptyWriteBuffer();
continue;
}
regsPtr->cstat = SII_SCH;
Mach_EmptyWriteBuffer();
return(SUCCESS);
}
/*
* Selection failed, clear all bus signals
*/
if (devSIIDebug > 0) {
printf("SelectTarget: Couldn't select target %d\n", devPtr->targetID);
}
if (devSIIDebug > 4) {
printf("Cstat = 0x%x\n", regsPtr->cstat);
}
regsPtr->cstat = SII_SCH;
regsPtr->comm = SII_DISCON;
Mach_EmptyWriteBuffer();
SII_WAIT_UNTIL((regsPtr->cstat & SII_SCH), SII_WAIT_COUNT, retval);
regsPtr->cstat = 0xffff;
regsPtr->dstat = 0xffff;
regsPtr->comm = 0;
Mach_EmptyWriteBuffer();
return(FAILURE);
}
/*
*----------------------------------------------------------------------
*
* SendCommand --
*
* Send a command to a SCSI controller via the SII.
*
* NOTE: The caller is assumed to have the master lock of the controller
* to which the device is attached held.
*
* NOTE2: We are always called with the master lock down so we don't
* have to worry about interrupts jumping in at the wrong time.
*
* Results:
* An error code.
*
* Side effects:
* Those of the command (Read, write etc.)
*
*----------------------------------------------------------------------
*/
static ReturnStatus
SendCommand(devPtr, scsiCmdPtr)
Device *devPtr; /* Device to sent to. */
ScsiCmd *scsiCmdPtr; /* Command to send. */
{
ReturnStatus status;
register char *charPtr;
Controller *ctrlPtr;
int size;
Address addr;
/*
* Set current active device and command for this controller.
*/
ctrlPtr = devPtr->ctrlPtr;
ctrlPtr->scsiCmdPtr = scsiCmdPtr;
ctrlPtr->devPtr = devPtr;
size = scsiCmdPtr->bufferLen;
addr = scsiCmdPtr->buffer;
/*
* Determine the DMA state the size and direction of data transfer.
*/
if (size == 0) {
ctrlPtr->dmaState = DMA_INACTIVE;
} else {
ctrlPtr->dmaState = (scsiCmdPtr->dataToDevice) ? DMA_SEND :
DMA_RECEIVE;
}
ctrlPtr->dmaStarted = FALSE;
if (devSIIDebug > 3) {
printf("SIICommand: %s cmd 0x%x addr %x size %d dma %s\n",
devPtr->handle.locationName, scsiCmdPtr->commandBlock[0], addr,
size, (ctrlPtr->dmaState == DMA_INACTIVE) ? "not active" :
((ctrlPtr->dmaState == DMA_SEND) ? "send" :
"receive"));
}
status = SelectTarget(devPtr);
if (status != SUCCESS) {
return(status);
}
if (devSIIDebug > 5) {
printf("SIICommand: %s waiting for command phase.\n",
devPtr->handle.locationName);
}
status = WaitPhase(ctrlPtr, CMD_PHASE, RESET);
if (status != SUCCESS) {
if (devSIIDebug > 0) {
printf("SII: wait on CMD_PHASE failed.\n");
}
return(status);
}
/*
* Stuff the control block through the commandStatus register.
*/
if (devSIIDebug > 5) {
printf("SCSI3Command: %s stuffing command of %d bytes.\n",
devPtr->handle.locationName, scsiCmdPtr->commandBlockLen);
}
charPtr = scsiCmdPtr->commandBlock;
if((unsigned char)charPtr & 0x1) {
panic("SendCommand: Misaligned scsi command\n");
}
status = StartDMA(ctrlPtr, DMA_SEND, TRUE, scsiCmdPtr->commandBlockLen,
charPtr);
return status;
}
/*
*----------------------------------------------------------------------
*
* StartDMA --
*
* Issue the sequence of commands to the controller to start DMA.
* If the wait parameter is TRUE then this procedure will wait
* until the DMA is finished and copy the results into the
* buffer. Otherwise it will just return and whoever handles the
* DMA interrupt will have to copy the data.
*
* NOTE: the data buffer should be word-aligned for DMA out.
*
* Results:
* SUCCESS if DMA was started (and possibly finished) correctly.
* DEV_TIMEOUT if we timed out waiting for the DMA to finish.
*
* Side effects:
* DMA is enabled. No registers other than the control register are
* to be accessed until DMA is disabled again.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
StartDMA(ctrlPtr, direction, wait, size, buffer)
register Controller *ctrlPtr; /* Controller */
Boolean wait; /* TRUE => wait for DMA to complete. */
int direction; /* DMA_SEND or DMA_RECEIVE. */
int size; /* # of bytes to transfer. */
char *buffer; /* Data buffer. */
{
volatile register SIIRegs *regsPtr;
register Device *devPtr;
unsigned short tmpPhase;
unsigned short tmpState;
devPtr = ctrlPtr->devPtr;
if (devSIIDebug > 4) {
printf("%s: StartDMA %s called size = %d.\n", ctrlPtr->name,
(direction == DMA_RECEIVE) ? "receive" :
((direction == DMA_SEND) ? "send" :
"not-active!"), size);
}
regsPtr = ctrlPtr->regsPtr;
regsPtr->comm &= ~(SII_INXFER | SII_DMA);
regsPtr->dstat = SII_DNE;
Mach_EmptyWriteBuffer();
if (direction == DMA_SEND) {
CopyToBuffer((unsigned short *)buffer,
(unsigned short *)(ctrlPtr->ramBuff + devPtr->buffOffset),
size);
Mach_EmptyWriteBuffer();
}
regsPtr->dmaddrl = devPtr->buffOffset & 0x00ffffff;
regsPtr->dmaddrh = (devPtr->buffOffset & 0x00ffffff) >> 16;
regsPtr->dmlotc = size;
Mach_EmptyWriteBuffer();
tmpPhase = regsPtr->dstat & SII_PHA_MSK;
tmpState = regsPtr->cstat & SII_STATE_MSK;
if (devSIIDebug > 4) {
printf("StartDMA: dstat is 0x%x.\n", regsPtr->dstat);
printf("StartDMA: cstat is 0x%x.\n", regsPtr->cstat);
printf("StartDMA: setting comm to 0x%x.\n",
SII_DMA | SII_INXFER | tmpState | tmpPhase);
}
regsPtr->comm = SII_DMA | SII_INXFER | tmpState | tmpPhase;
Mach_EmptyWriteBuffer();
if (wait) {
int retval;
/*
* Wait for the DMA to complete.
*/
SII_WAIT_UNTIL((regsPtr->dstat & SII_DNE), SII_WAIT_COUNT, retval);
regsPtr->comm &= ~(SII_INXFER | SII_DMA);
if (!(regsPtr->dstat & SII_DNE)) {
printf("StartDMA: DMA failed\n");
}
if (retval >= SII_WAIT_COUNT) {
return (DEV_TIMEOUT);
}
regsPtr->dstat = SII_DNE;
regsPtr->dmlotc = 0;
Mach_EmptyWriteBuffer();
if (direction == DMA_RECEIVE) {
CopyFromBuffer(
(unsigned short *)(ctrlPtr->ramBuff+devPtr->buffOffset),
(unsigned char *) buffer, size);
}
} else {
ctrlPtr->dmaStarted = TRUE;
}
return (SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* GetStatusByte --
*
* Complete an SCSI command by getting the status bytes from
* the device and waiting for the ``command complete''
* message that follows the status bytes.
*
* Results:
* An error code if the status didn't come through or it
* indicated an error.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
GetStatusByte(ctrlPtr,statusBytePtr)
Controller *ctrlPtr; /* Controller to get byte from. */
unsigned char *statusBytePtr; /* Where to put the status byte. */
{
ReturnStatus status;
char message;
if (devSIIDebug > 4) {
printf("GetStatusByte called ");
}
*statusBytePtr = 0;
/*
* After the DATA_IN/OUT phase we enter the STATUS phase for
* 1 byte (usually) of status. This is followed by the MESSAGE phase
*/
status = WaitPhase(ctrlPtr, STATUS_PHASE, RESET);
if (status != SUCCESS) {
if (devSIIDebug > 3) {
printf("Warning: %s wait on PHASE_STATUS failed.\n",ctrlPtr->name);
}
return(status);
}
/*
* Get one status byte.
*/
status = StartDMA(ctrlPtr, DMA_RECEIVE, TRUE, 1, (char *)statusBytePtr);
if (status != SUCCESS) {
printf("Warning: %s error 0x%x getting status byte\n",
ctrlPtr->name, status);
return (status);
}
if (devSIIDebug > 4) {
printf("got 0x%x\n", *statusBytePtr);
}
/*
* Wait for the message in phase and grab the COMMAND COMPLETE message
* off the bus.
*/
status = WaitPhase(ctrlPtr, MSG_IN_PHASE, RESET);
if (status != SUCCESS) {
printf("Warning: %s wait on PHASE_MSG_IN after status failed.\n",
ctrlPtr->name);
return(status);
}
status = StartDMA(ctrlPtr, DMA_RECEIVE, TRUE, 1, &message);
if (status != SUCCESS) {
printf("Warning: %s got error 0x%x getting message and status.\n",
ctrlPtr->name, status);
return(status);
}
if (message != SCSI_COMMAND_COMPLETE) {
printf("Warning: %s message %d after status is not command complete.\n", ctrlPtr->name, message);
return(FAILURE);
}
if (devSIIDebug > 4) {
printf("Got message 0x%x\n", message);
}
return(SUCCESS);
}
#define WAIT_LENGTH 500000
/*
*----------------------------------------------------------------------
*
* WaitPhase --
*
* Wait for a phase to be signalled in the controller registers.
* This is a specialized version of WaitReg, which compares
* all the phase bits to make sure the phase is exactly what is
* requested and not something that matches only in some bits.
*
* Results:
* SUCCESS if the condition occurred before a threshold time limit,
* DEV_TIMEOUT otherwise.
*
* Side effects:
* This resets the SCSI bus if the condition bits are not set by
* the controller before timeout.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
WaitPhase(ctrlPtr, phase, reset)
Controller *ctrlPtr; /* Controller state */
unsigned char phase; /* phase to check */
Boolean reset; /* whether to reset the bus on error */
{
volatile register SIIRegs *regsPtr = (SIIRegs *)ctrlPtr->regsPtr;
register int i;
ReturnStatus status = DEV_TIMEOUT;
register unsigned short dstatReg;
for (i=0 ; i < WAIT_LENGTH ; i++) {
dstatReg = regsPtr->dstat;
if (devSIIDebug > 10 && i < 5) {
printf("%d/%x ", i, dstatReg);
}
if ((dstatReg & SII_PHA_MSK) == phase) {
return(SUCCESS);
}
}
if (devSIIDebug > 5) {
printf("WaitPhase: timed out.\n");
PrintRegs(regsPtr);
printf("WaitPhase: was checking for phase 0x%x.\n",
(int) phase);
printf("WaitPhase: dstat is 0x%x.\n", dstatReg);
}
if (reset) {
Reset(ctrlPtr);
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* PrintRegs --
*
* Print out the interesting registers. This could be a macro but
* then it couldn't be called from kdbx. This routine is necessary
* because kdbx doesn't print all the character values properly.
*
* Results:
* None.
*
* Side effects:
* Data is displayed on the console or to the debugger.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
static void
PrintRegs(regsPtr)
volatile SIIRegs *regsPtr;
{
}
/*
*----------------------------------------------------------------------
*
* SpecialSenseProc --
*
* Special function used for HBA generated REQUEST SENSE. A SCSI
* command request with this function as a call back proc will
* be processed by routine RequestDone as a result of a
* REQUEST SENSE. This routine is never called.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
SpecialSenseProc()
{
}
/*
*----------------------------------------------------------------------
*
* RequestDone --
*
* Process a request that has finished. Unless a SCSI check condition
* bit is present in the status returned, the request call back
* function is called. If check condition is set we fire off a
* SCSI REQUEST SENSE to get the error sense bytes from the device.
*
* Results:
* None.
*
* Side effects:
* The call back function may be called.
*
*----------------------------------------------------------------------
*/
void
RequestDone(devPtr, scsiCmdPtr, status, scsiStatusByte, amountTransferred)
Device *devPtr; /* Device for request. */
ScsiCmd *scsiCmdPtr; /* Request that finished. */
ReturnStatus status; /* Status returned. */
unsigned char scsiStatusByte; /* SCSI Status Byte. */
int amountTransferred; /* Amount transferred by
* command. */
{
ReturnStatus senseStatus;
Controller *ctrlPtr = devPtr->ctrlPtr;
if (devSIIDebug > 3) {
printf("RequestDone for %s status 0x%x scsistatus 0x%x count %d\n",
devPtr->handle.locationName, status,scsiStatusByte,
amountTransferred);
}
/*
* First check to see if this is the reponse of a HBA generated
* REQUEST SENSE command. If this is the case, we can process
* the callback of the frozen command for this device and
* allow the flow of command to the device to be resummed.
*/
if (scsiCmdPtr->doneProc == SpecialSenseProc) {
(devPtr->frozen.scsiCmdPtr->doneProc)(devPtr->frozen.scsiCmdPtr,
devPtr->frozen.status,
devPtr->frozen.statusByte,
devPtr->frozen.amountTransferred,
amountTransferred,
devPtr->senseBuffer);
ctrlPtr->scsiCmdPtr = (ScsiCmd *)NIL;
return;
}
/*
* This must be an outside request finishing. If the request
* suffered an error or the HBA or the scsi status byte
* says there is no error sense present, we can do the
* callback and free the controller.
*/
if ((status != SUCCESS) || !SCSI_CHECK_STATUS(scsiStatusByte)) {
(scsiCmdPtr->doneProc)(scsiCmdPtr, status, scsiStatusByte,
amountTransferred, 0, (char *) 0);
ctrlPtr->scsiCmdPtr = (ScsiCmd *) NIL;
return;
}
if (status != SUCCESS) {
amountTransferred = 0;
}
/*
* If we got here than the SCSI command came back from the device
* with the CHECK bit set in the status byte.
* Need to perform a REQUEST SENSE. Move the current request
* into the frozen state and issue a REQUEST SENSE.
*/
MASTER_LOCK(&(ctrlPtr->mutex));
devPtr->frozen.scsiCmdPtr = scsiCmdPtr;
devPtr->frozen.statusByte = scsiStatusByte;
devPtr->frozen.amountTransferred = amountTransferred;
devPtr->frozen.status = status;
DevScsiSenseCmd((ScsiDevice *)devPtr, DEV_MAX_SENSE_BYTES,
devPtr->senseBuffer, &(devPtr->SenseCmd));
devPtr->SenseCmd.doneProc = SpecialSenseProc,
senseStatus = SendCommand(devPtr, &(devPtr->SenseCmd));
MASTER_UNLOCK(&(ctrlPtr->mutex));
/*
* If we got an HBA error on the REQUEST SENSE we end the outside
* command with the SUCCESS status but zero sense bytes returned.
*/
if (senseStatus != SUCCESS) {
(scsiCmdPtr->doneProc)(scsiCmdPtr, status, scsiStatusByte,
amountTransferred, 0, (char *) 0);
ctrlPtr->scsiCmdPtr = (ScsiCmd *) NIL;
}
}
/*
*----------------------------------------------------------------------
*
* entryAvailProc --
*
* Act upon an entry becomming available in the queue for this
* controller. This routine is the Dev_Queue callback function that
* is called whenever work becomes available for this controller.
* If the controller is not already busy we dequeue and start the
* request.
* NOTE: This routine is also called from Dev_SIIIntr to start the
* next request after the previously one finishes.
*
* Results:
* None.
*
* Side effects:
* Request may be dequeue and submitted to the device. Request callback
* function may be called.
*
*----------------------------------------------------------------------
*/
static Boolean
entryAvailProc(clientData, newRequestPtr)
ClientData clientData; /* Really the Device this request ready. */
List_Links *newRequestPtr; /* The new SCSI request. */
{
register Device *devPtr;
register Controller *ctrlPtr;
register ScsiCmd *scsiCmdPtr;
ReturnStatus status;
static Address notifications[100];
static Address sent[100];
static Address bailed[100];
static Address cause[100];
static Address failed[100];
static int nctr = 0;
static int sctr = 0;
static int bctr = 0;
static int fctr = 0;
#define INC(a) { (a) = ((a) + 1) % 100; }
devPtr = (Device *) clientData;
ctrlPtr = devPtr->ctrlPtr;
/*
* If we are busy (have an active request) just return. Otherwise
* start the request.
*/
notifications[nctr] = (Address) newRequestPtr;
INC(nctr);
if (ctrlPtr->scsiCmdPtr != (ScsiCmd *) NIL) {
bailed[bctr] = (Address) newRequestPtr;
cause[bctr] = (Address) ctrlPtr->scsiCmdPtr;
INC(bctr);
return FALSE;
}
again:
scsiCmdPtr = (ScsiCmd *) newRequestPtr;
devPtr = (Device *) clientData;
sent[sctr] = (Address) scsiCmdPtr;
INC(sctr);
status = SendCommand((Device *) devPtr, scsiCmdPtr);
/*
* If the command couldn't be started do the callback function.
*/
if (status != SUCCESS) {
failed[fctr] = (Address) scsiCmdPtr;
INC(fctr);
MASTER_UNLOCK(&(ctrlPtr->mutex));
RequestDone(devPtr,scsiCmdPtr,status,0,0);
MASTER_LOCK(&(ctrlPtr->mutex));
}
if (ctrlPtr->scsiCmdPtr == (ScsiCmd *) NIL) {
newRequestPtr = Dev_QueueGetNextFromSet(ctrlPtr->devQueues,
DEV_QUEUE_ANY_QUEUE_MASK,&clientData);
if (newRequestPtr != (List_Links *) NIL) {
goto again;
}
}
return TRUE;
}
/*
*----------------------------------------------------------------------
*
* Dev_SIIIntr --
*
* Handle interrupts from the SII controller.
*
* Results:
* TRUE if an SII controller was responsible for the interrupt
* and this routine handled it.
*
* Side effects:
* Usually a process is notified that an I/O has completed.
*
*----------------------------------------------------------------------
*/
Boolean
Dev_SIIIntr()
{
Controller *ctrlPtr;
volatile register SIIRegs *regsPtr;
Device *devPtr;
unsigned char statusByte = 0;
ReturnStatus status;
register int i;
Boolean found;
List_Links *newRequestPtr;
ClientData clientData;
unsigned short cstat;
unsigned short dstat;
if (devSIIDebug > 4) {
printf("DevSIIIntr: ");
}
found = FALSE;
/*
* Find which controller caused the interrupt.
*/
for (i = 0; i < numSIIControllers; i++) {
ctrlPtr = controllers[i];
if (ctrlPtr == (Controller *) 0) {
continue;
}
regsPtr = (SIIRegs *)ctrlPtr->regsPtr;
devPtr = ctrlPtr->devPtr;
cstat = regsPtr->cstat;
dstat = regsPtr->dstat;
if (cstat & (SII_CI | SII_DI)) {
found = TRUE;
break;
}
}
if (!found) {
if (devSIIDebug > 4 ) {
printf("spurious\n");
}
return FALSE;
}
/*
* Acknowledge everything.
*/
regsPtr->cstat = cstat;
regsPtr->dstat = dstat;
Mach_EmptyWriteBuffer();
/*
* Check for a BUS ERROR
*/
if(cstat & SII_BER) {
if (devSIIDebug > 4) {
printf("Bus error");
}
}
/*
* Check for a PARITY ERROR
*/
if(dstat & SII_IPE) {
printf("Dev_SIIIntr: Parity error!!\n");
goto rtnHardErrorAndGetNext;
}
/*
* Check for a BUS RESET
*/
if(cstat & SII_RST_ONBUS) {
printf("Dev_SIIIntr: Bus reset!!\n");
goto rtnHardErrorAndGetNext;
}
/*
* Check for a state change.
*/
if (cstat & SII_SCH) {
if (devSIIDebug > 4) {
printf("State change ");
}
}
/*
* Check for DMA completion.
*/
if (dstat & SII_DNE) {
if (devSIIDebug > 4) {
printf("DMA complete ");
}
}
/*
* Check for phase change.
*/
if (dstat & SII_MIS) {
if (devSIIDebug > 4) {
printf("Phase change ");
}
switch (dstat & SII_PHA_MSK) {
case DATA_IN_PHASE:
case DATA_OUT_PHASE: {
if (devSIIDebug > 4) {
printf("Data Phase Interrupt\n");
}
status = StartDMA(ctrlPtr, ctrlPtr->dmaState, FALSE,
ctrlPtr->scsiCmdPtr->bufferLen,
ctrlPtr->scsiCmdPtr->buffer);
if (status != SUCCESS) {
printf("Warning: couldn't start dma.\n");
}
return(TRUE);
}
case MSG_IN_PHASE: {
char message;
status = StartDMA(ctrlPtr, DMA_RECEIVE, TRUE, 1, &message);
if (devSIIDebug > 4) {
printf("Msg Phase Interrupt\n");
}
if (status != SUCCESS) {
printf("Warning: %s couldn't get message.\n",ctrlPtr->name);
return(TRUE);
}
if (message != SCSI_COMMAND_COMPLETE) {
printf("Warning: %s couldn't handle message 0x%x from %s.\n",
ctrlPtr->name, message,
ctrlPtr->devPtr->handle.locationName);
}
return(TRUE);
}
case STATUS_PHASE: {
if (devSIIDebug > 4) {
printf("Status Phase Interrupt\n");
}
if ((ctrlPtr->dmaStarted == TRUE) &&
(ctrlPtr->dmaState == DMA_RECEIVE)) {
/*
* We just transitioned from the data phase so copy
* in the data.
*/
CopyFromBuffer((unsigned short *)(ctrlPtr->ramBuff +
devPtr->buffOffset),
(unsigned char *)ctrlPtr->scsiCmdPtr->buffer,
(int)(ctrlPtr->scsiCmdPtr->bufferLen - regsPtr->dmlotc));
ctrlPtr->dmaStarted = FALSE;
}
status = GetStatusByte(ctrlPtr, &statusByte);
if (status != SUCCESS) {
/* Return the hard error message
* to the call and get the next
* entry in the devQueue. */
goto rtnHardErrorAndGetNext;
}
RequestDone(devPtr, ctrlPtr->scsiCmdPtr, status, statusByte,
(int)(ctrlPtr->scsiCmdPtr->bufferLen - regsPtr->dmlotc));
if (ctrlPtr->scsiCmdPtr == (ScsiCmd *)NIL) {
newRequestPtr = Dev_QueueGetNextFromSet(ctrlPtr->devQueues,
DEV_QUEUE_ANY_QUEUE_MASK,&clientData);
if (newRequestPtr != (List_Links *) NIL) {
MASTER_LOCK(&(ctrlPtr->mutex));
entryAvailProc(clientData,newRequestPtr);
MASTER_UNLOCK(&(ctrlPtr->mutex));
}
}
return(TRUE);
}
default: {
printf("Warning: %s couldn't handle phase %x... ignoring.\n",
ctrlPtr->name, dstat & SII_PHA_MSK);
if (devSIIDebug > 0) {
PrintRegs(regsPtr);
}
return(TRUE);
}
}
}
if (devSIIDebug > 4) {
printf("\n");
}
return(TRUE);
/*
* Jump here to return an error and reset the HBA.
*/
rtnHardErrorAndGetNext:
printf("Warning: %s reset and current command terminated.\n",
devPtr->handle.locationName);
if (ctrlPtr->scsiCmdPtr != (ScsiCmd *) NIL) {
RequestDone(devPtr,ctrlPtr->scsiCmdPtr,status,statusByte,0);
}
Reset(ctrlPtr);
/*
* Use the queue entryAvailProc to start the next request for this device.
*/
newRequestPtr = Dev_QueueGetNextFromSet(ctrlPtr->devQueues,
DEV_QUEUE_ANY_QUEUE_MASK,&clientData);
if (newRequestPtr != (List_Links *) NIL) {
MASTER_LOCK(&(ctrlPtr->mutex));
entryAvailProc(clientData,newRequestPtr);
MASTER_UNLOCK(&(ctrlPtr->mutex));
}
return (TRUE);
}
/*
*----------------------------------------------------------------------
*
* ReleaseProc --
*
* Device release proc for controller.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
static ReturnStatus
ReleaseProc(scsiDevicePtr)
ScsiDevice *scsiDevicePtr;
{
}
/*
*----------------------------------------------------------------------
*
* DevSIIInit --
*
* Check for the existant of the SII controller. If it
* exists allocate data stuctures for it.
*
* Results:
* TRUE if the controller exists, FALSE otherwise.
*
* Side effects:
* Memory may be allocated.
*
*----------------------------------------------------------------------
*/
ClientData
DevSIIInit(ctrlLocPtr)
DevConfigController *ctrlLocPtr; /* Controller location. */
{
int ctrlNum;
Controller *ctrlPtr;
int i,j;
ctrlNum = ctrlLocPtr->controllerID;
/*
* It's there. Allocate and fill in the Controller structure.
*/
if (ctrlNum+1 > numSIIControllers) {
numSIIControllers = ctrlNum + 1;
}
controllers[ctrlNum] = ctrlPtr = (Controller *) malloc(sizeof(Controller));
bzero((char *) ctrlPtr, sizeof(Controller));
ctrlPtr->regsPtr = (SIIRegs *) (ctrlLocPtr->address);
ctrlPtr->ramBuff = SII_BUF_BASE;
ctrlPtr->name = ctrlLocPtr->name;
Sync_SemInitDynamic(&(ctrlPtr->mutex),ctrlPtr->name);
/*
* Initialized the name, device queue header, and the master lock.
* The controller comes up with no devices active and no devices
* attached. Reserved the devices associated with the
* targetID of the controller (7).
*/
ctrlPtr->devQueues = Dev_CtrlQueuesCreate(&(ctrlPtr->mutex),entryAvailProc);
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
ctrlPtr->devicePtr[i][j] = (i == 7) ? (Device *) 0 : (Device *) NIL;
}
}
ctrlPtr->scsiCmdPtr = (ScsiCmd *) NIL;
Reset(ctrlPtr);
return((ClientData) ctrlPtr);
}
/*
* Offset into DMA buffer. We partition the DMA buffer into seperate
* regions for each device. We have to have seperate regions if we
* ever want disconnect/reconnect.
*
* IMPORTANT!!!
* For some reason DMA doesn't work if this offset is anything other than
* 0. Do not increment it when attaching devices.
*/
static unsigned ramBuffOffset = 0;
/*
*----------------------------------------------------------------------
*
* DevSIIDevice --
*
* Attach a SCSI device using the SII HBA.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ScsiDevice *
DevSIIAttachDevice(devicePtr, insertProc)
Fs_Device *devicePtr; /* Device to attach. */
void (*insertProc)(); /* Queue insert procedure. */
{
Device *devPtr;
Controller *ctrlPtr;
char tmpBuffer[512];
int length;
int ctrlNum;
int targetID, lun;
extern char *strcpy();
/*
* First find the SII controller this device is on.
*/
ctrlNum = SCSI_HBA_NUMBER(devicePtr);
if ((ctrlNum > MAX_SII_CTRLS) ||
(controllers[ctrlNum] == (Controller *) 0)) {
return (ScsiDevice *) NIL;
}
ctrlPtr = controllers[ctrlNum];
/*
* See if the device is already present.
*/
targetID = SCSI_TARGET_ID(devicePtr);
lun = SCSI_LUN(devicePtr);
MASTER_LOCK(&(ctrlPtr->mutex));
/*
* A device pointer of zero means that targetID/LUN
* conflicts with that of the HBA. A NIL means the
* device hasn't been attached yet.
*/
if (ctrlPtr->devicePtr[targetID][lun] == (Device *) 0) {
MASTER_UNLOCK(&(ctrlPtr->mutex));
return (ScsiDevice *) NIL;
}
if (ctrlPtr->devicePtr[targetID][lun] != (Device *) NIL) {
MASTER_UNLOCK(&(ctrlPtr->mutex));
return (ScsiDevice *) (ctrlPtr->devicePtr[targetID][lun]);
}
/*
* Initialize the device struct.
*/
devPtr = (Device *) malloc(sizeof(Device));
bzero((char *) devPtr, sizeof(Device));
devPtr->targetID = targetID;
devPtr->ctrlPtr = ctrlPtr;
devPtr->buffOffset = ramBuffOffset;
ctrlPtr->devPtr = (Device *)NIL;
ctrlPtr->scsiCmdPtr = (ScsiCmd *)NIL;
ctrlPtr->devicePtr[targetID][lun] = devPtr;
/*
* Until we support disconnect/reconnect all device queues are
* stored under the same queueBit
*/
devPtr->handle.devQueue = Dev_QueueCreate(ctrlPtr->devQueues,
1, insertProc, (ClientData) devPtr);
devPtr->handle.releaseProc = ReleaseProc;
devPtr->handle.LUN = lun;
devPtr->handle.maxTransferSize = SII_MAX_DMA_XFER_LENGTH;
(void) sprintf(tmpBuffer, "%s#%d Target %d LUN %d", ctrlPtr->name, ctrlNum,
devPtr->targetID, devPtr->handle.LUN);
length = strlen(tmpBuffer);
devPtr->handle.locationName = (char *) strcpy(malloc(length+1),tmpBuffer);
/*
* IMPORTANT: the following statement is ifdef'd out for a reason.
* See comment above.
*/
#if 0
ramBuffOffset += SII_MAX_DMA_XFER_LENGTH * 2;
#endif
MASTER_UNLOCK(&(ctrlPtr->mutex));
return (ScsiDevice *) devPtr;
}
/*
*----------------------------------------------------------------------
*
* CopyToBuffer --
*
* Copy data to the dma buffer. The dma buffer can only be written
* one word at a time.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
CopyToBuffer(src, dst, length)
register volatile unsigned short *src;
register volatile unsigned short *dst;
register int length;
{
while(length > 0) {
*dst++ = *src++;
dst++;
length -= 2;
}
}
/*
*----------------------------------------------------------------------
*
* CopyFromBuffer --
*
* Copy data from the dma buffer. The dma buffer can only be read
* one word at a time.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
CopyFromBuffer(src, dst, length)
volatile register unsigned short *src;
volatile register unsigned char *dst;
register int length;
{
if((int)dst & 0x01) {
while(length > 1) {
*dst++ = *src & 0xff;
*dst++ = (*src >> 8) & 0xff;
src += 2;
length -= 2;
}
if (length == 1) {
/*
* Handle an odd length. Shove the last byte out.
*/
*dst = *src & 0xff;
}
} else {
register unsigned short *wdst = (unsigned short *)dst;
register int i;
for(i = 0; i<length-16; i+=16) {
*wdst = *src;
*(wdst+1) = *(src+2);
*(wdst+2) = *(src+4);
*(wdst+3) = *(src+6);
*(wdst+4) = *(src+8);
*(wdst+5) = *(src+10);
*(wdst+6) = *(src+12);
*(wdst+7) = *(src+14);
src += 16;
wdst += 8;
}
while(i < length - 1) {
*wdst++ = *src;
src += 2;
i += 2;
}
if (i == length - 1) {
/*
* Handle an odd length. Shove the last byte out.
*/
*dst = *src & 0xff;
}
}
}